package org.timemachine.tool;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;

import javax.persistence.Id;

import org.timemachine.tool.spi.ClassHelper;

public class ClassHelperImpl implements ClassHelper {
	
	public Object findFieldValue(Object obj, Class<?> clazz, String field) throws Exception {
		
        Field fieldClasses = clazz.getDeclaredField(field);
        fieldClasses.setAccessible(true);
		return fieldClasses.get(obj);
		
	}

	@SuppressWarnings({ "rawtypes" })
	public Annotation[] findAnnotations(Vector<Class<?>> clazzes) throws Exception {
        
		Annotation[] annotations = new Annotation[0];
        for (Iterator<Class<?>> iter = clazzes.iterator(); iter.hasNext();) {
            System.out.println("Loaded " + iter.next());
            Class clazz = iter.next();
            annotations = clazz.getAnnotations();
            for (int i = 0; i < annotations.length; i++) {
	            System.out.println("   - " + annotations[i].toString());
			}
        }
        return annotations;
	}
	
	public boolean hasAnnotation(Class<?> clazz, Class<? extends Annotation> aClass) throws Exception {
		try {
			clazz.getAnnotation(aClass);			
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	public Set<Class<?>> findClassesWithAnnotation(Vector<Class<?>> clazzes, Set<Class<? extends Annotation>> annotationClazzes) {

		Set<Class<?>> setAnnos = new HashSet<Class<?>>();
		
		for (Class<?> clazz : clazzes) {
			int annoCount = 0;
        	for (Class<? extends Annotation> annotationClazz : annotationClazzes) {
                if (clazz.isAnnotationPresent(annotationClazz)) {
                	annoCount++;
                } 
			}
        	if (annoCount == annotationClazzes.size()) {
        		setAnnos.add(clazz);
        	}
        }
		
		return setAnnos;
	}
	

	public String findPrimaryKeyName(Object object) {
		Field[] fields = findAllDeclaredFieldsWithSuperClasses(object.getClass());
		for (Field field : fields) {
			Object obj = field.getAnnotation(Id.class);
			if (obj!=null) {
				field.setAccessible(true);
				return field.getName();
			}
		}
		return null;
	}

	public Object findPrimaryKeyValue(Object object) {
		try {
			Field[] fields = findAllDeclaredFieldsWithSuperClasses(object.getClass());
			for (Field field : fields) {
				Id id = field.getAnnotation(Id.class);
				if (id!=null) {
					field.setAccessible(true);
					return field.get(object);
				}
			}			
		} catch (Exception e) {
			System.out.println(e);
		}
		return null;
	}
	
	@SuppressWarnings("rawtypes")
	public Field[] findAllDeclaredFieldsWithSuperClasses(Class c) {
		Field[] allFields = new Field[0];
		while (c != null) {
			allFields = mergeFields(allFields, c.getDeclaredFields());
			c = c.getSuperclass();
		}
		return allFields;
	}

	public Field[] mergeFields(final Field[]... arrays) {
		int size = 0;
		for (Field[] a : arrays) {
			size += a.length;
		}
		Field[] res = new Field[size];

		int destPos = 0;
		for (int i = 0; i < arrays.length; i++) {
			if (i > 0) {
				destPos += arrays[i - 1].length;
			}
			int length = arrays[i].length;
			System.arraycopy(arrays[i], 0, res, destPos, length);
		}
		return res;
	}

}
